#include "utils/xmlrpc_helper.h"
#include "utils/common_utils.h"
#include "utils/utils.h"
#include "utils/string_cast.h"
#include "logger.h"
#include <xmlrpc/XmlRpcValue.h>
#include <xmlrpc/XmlRpcClient.h>
#include <xmlrpc/XmlRpcException.h>

#define CHECK_RESULT_ARRAY if (result.getType() != XmlRpc::XmlRpcValue::TypeArray) { ROS_ERROR("error: result is not array");return false;}
#define CHECK_RESULT_SIZE(count) if(result.size() != count) { ROS_ERROR("error: result size is not %d", count); return false; }

XmlrpcHelper::XmlrpcHelper()
{
    std::lock_guard<std::mutex> lockGuard(clientMutex);
    std::string rosMasterUri = CommonUtils::getEnv("ROS_MASTER_URI", "http://127.0.0.1:11311");
    std::string host;
    int port;
    if (!CommonUtils::parserHostPort(rosMasterUri, host, port))
    {
        ROS_ERROR("ROS_MASTER_URI=%s, xmlrpc client init fail", rosMasterUri.c_str());
        return;
    }
    xmlRpcClient = std::make_unique<XmlRpc::XmlRpcClient>(host.c_str(), port);
}
XmlrpcHelper::~XmlrpcHelper()
{
    std::lock_guard<std::mutex> lockGuard(clientMutex);
    if (xmlRpcClient)
    {
        xmlRpcClient->close();
    }
}

XmlrpcHelper *XmlrpcHelper::getXmlrpcHelperInstance()
{
    static XmlrpcHelper xmlrpcHelper;
    return &xmlrpcHelper;
}
bool XmlrpcHelper::saveParameterToServer(const std::string &name, const std::string &value)
{
    std::lock_guard<std::mutex> lockGuard(clientMutex);
    if (!xmlRpcClient)
    {
        return false;
    }
    XmlRpc::XmlRpcValue args, result;
    args[0] = "/roslaunch";
    args[1] = name;

    XmlRpc::XmlRpcValue xmlValue;
    if (Utils::isBool(value))
    {
        args[2] = XmlRpc::XmlRpcValue(value == "true");
    }
    else if (Utils::isDouble(value))
    {
        args[2] = XmlRpc::XmlRpcValue(utils::StringCast::toDouble(value));
    }
    else if (Utils::isInt(value))
    {
        args[2] = XmlRpc::XmlRpcValue(utils::StringCast::toInt(value));
    }
    else
    {
        args[2] = value;
    }
    if (!xmlRpcClient->execute("setParam", args, result))
    {
        ROS_ERROR("execute setParam fail");
        return false;
    }
    try
    {
        CHECK_RESULT_ARRAY
        CHECK_RESULT_SIZE(3)

        auto code = result[0].operator int &();
        auto msg = result[1].operator std::string &();

        ROS_INFO("setParam, %s:%s pass, code: %d, msg: %s", name.c_str(), value.c_str(), code, msg.c_str());
    }
    catch (XmlRpc::XmlRpcException &e)
    {
        ROS_ERROR("setParam return error, %s", e.getMessage().c_str());
        return false;
    }
    return true;
}
